//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// Copyright 2012 Microsoft Corporation. All Rights Reserved
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.   
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

namespace Preflight.Core
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    using Preflight.Core.Execution;
    using Preflight.Model;

    public class PreflightEngineFactory
    {
        public IPreflightEngine CreateEngine(PreflightContext context)
        {
            this.ValidateContext(context);
            Log.LogInformation("Creating engine with these settings: {0}", context.Options);
            var proxyFactory = this.CreateProxyFactory(context);
            var verifications = proxyFactory.GetVerifications();
            var applicationDetail = this.CreateApplicationDetail(proxyFactory.ApplicationDescription, verifications);
            var verificationExecution = this.CreateVerificationExecution(context);
            var realEngine = this.CreatePreflightEngine(verifications, applicationDetail, verificationExecution);
            var engine = this.DecorateWithCachingIfEnabled(realEngine, context);
            engine = this.DecorateWithThrolling(engine, context);
            Log.LogInformation("Engine created!");
            return engine;
        }

        private void ValidateContext(PreflightContext context)
        {
            Log.LogVerbose("Validating context");
            if (context == null)
            {
                Log.LogError("Factory cannot create engine with a null context");
                throw new ArgumentNullException("context");
            }

            if (context.VerificationFactory == null)
            {
                Log.LogError("Factory cannot create engine. No verification factory provided");
                throw new ArgumentException("VerificationFactory is null. Engine cannot be created", "context");
            }

            if (context.Options == null)
            {
                Log.LogError("Factory cannot create engine. PreflightOptions are required");
                throw new ArgumentException("PreflightOptions property is null. Engine cannot be created", "context");
            }
            
            this.ValidateOptions(
                ErrorMessages.InvalidCachingOptions,
                context.Options,
                options => options.EnableResultCaching && options.ResultCachingDuration == default(TimeSpan));

            this.ValidateOptions(
                ErrorMessages.InvalidMaxConcurrentRequests,
                context.Options,
                options => options.MaximumNumberOfConcurrentRequests < 1);

             this.ValidateOptions(
                ErrorMessages.InvalidVerificationTimeout,
                context.Options,
                options => options.VerificationTimeout == default(TimeSpan));

             Log.LogVerbose("Context validation complete!");
        }

        private void ValidateOptions(string errorMessage, PreflightOptions options, Func<PreflightOptions, bool> invalidCondition)
        {
            if (invalidCondition(options))
            {
                Log.LogError(errorMessage);
                throw new InvalidOperationException(errorMessage);
            }
        }

        private IPreflightEngine DecorateWithCachingIfEnabled(IPreflightEngine engine, PreflightContext context)
        {
            if (context.Options.EnableResultCaching)
            {
                Log.LogVerbose("Caching is enabled. Adding caching functionality...");
                return new CacheEngineResults(engine, context.Options);
            }

            return engine;
        }

        private IPreflightEngine DecorateWithThrolling(IPreflightEngine engine, PreflightContext context)
        {
            Log.LogVerbose("Enabling throttling");
            return new EngineRequestThrottler(engine, context.Options);
        }

        private IVerificationExecutor CreateVerificationExecution(PreflightContext context)
        {
            IVerificationExecutor executeStrategy = null;

            if (context.Options.SequentialMode)
            {
                Log.LogVerbose("Using sequential execution mode");
                executeStrategy = new VerificationsExecutorSequentially(context.Options);
            }
            else
            {
                Log.LogVerbose("Using parallel execution mode");
                executeStrategy = new VerificationsExecutorInParallel(context.Options);
            }

            return new VerificationExecutorExceptionCatchAll(executeStrategy);
        }

        private ApplicationDetail CreateApplicationDetail(ApplicationDescription description, IEnumerable<IVerification> verifications)
        {
            return new ApplicationDetail
                {
                    Description = description,
                    TestCategories = verifications.Select(item => item.Category).Distinct().ToList()
                };
        }

        private IPreflightEngine CreatePreflightEngine(IEnumerable<IVerification> verifications, ApplicationDetail applicationDetail, IVerificationExecutor verificationExecution)
        {
            return new PreflightEngine(verificationExecution, verifications, applicationDetail);
        }

        private IVerificationFactory CreateProxyFactory(PreflightContext context)
        {
            Log.LogVerbose("Proxying supplied factory");
            return new VerificationFactoryProxy(context.VerificationFactory);
        }

        private class ErrorMessages
        {
            public const string InvalidCachingOptions = "Invalid options. Caching is enabled but the duration is 0";
            public const string InvalidVerificationTimeout = "Invalid options. Cannot have a verification timeout of 0";
            public const string InvalidMaxConcurrentRequests = "Invalid MaximunNumberOfConcurrentRequests (PreflightOptions). Cannot be less than 1";
        }
    }
}
